package com.ruoyi.gateway.service.impl;

import com.ruoyi.gateway.domain.DeviceMap;
import com.ruoyi.gateway.domain.dto.MessageFlowRecord;
import com.ruoyi.gateway.domain.dto.MessageTransformResult;
import com.ruoyi.gateway.domain.dto.TopicTransformResult;
import com.ruoyi.gateway.service.*;
import lombok.extern.slf4j.Slf4j;
import org.dromara.mica.mqtt.codec.MqttQoS;
import org.dromara.mica.mqtt.core.annotation.MqttClientSubscribe;
import org.dromara.mica.mqtt.spring.client.MqttClientTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Service
@Slf4j
public class MqttClientManagerImpl implements MqttClientManager {

    @Autowired
//    @Qualifier("platformMqttClientTemplate")
    private MqttClientTemplate mqttClientTemplate;

    @Autowired
    private IDeviceMapService deviceMappingService;

    @Autowired
    private ITopicMapService topicMappingService;

    @Autowired
    private MessageTransformService messageTransformService;

    @Autowired
    private MqttServerService mqttServerService;

    @Autowired
    private GatewayMonitorService monitorService;

    // 存储设备与平台客户端的映射关系
    private final Map<String, List<PlatformClientInfo>> devicePlatformClientsMap = new ConcurrentHashMap<>();

    /**
     * 为设备创建平台代理客户端
     */
    @Override
    public void createPlatformClients(String deviceClientId) {
        try {
            log.info("为设备 {} 创建平台代理客户端", deviceClientId);

            // 获取设备的所有映射关系
            List<DeviceMap> deviceMappings = deviceMappingService.getDeviceMappingsByClientId(deviceClientId);
            if (deviceMappings == null || deviceMappings.isEmpty()) {
                log.warn("设备 {} 没有找到映射配置", deviceClientId);
                return;
            }

            // 为每个设备映射创建平台订阅
            for (DeviceMap mapping : deviceMappings) {
                try {
                    subscribePlatformTopics(mapping);
                    monitorService.recordPlatformClientConnect(mapping.getPlatformClientId(), deviceClientId);
                    log.info("为设备 {} 创建平台代理客户端成功: {}", deviceClientId, mapping.getPlatformClientId());
                } catch (Exception e) {
                    log.error("为设备 {} 创建平台代理客户端失败: {}", deviceClientId, mapping.getPlatformClientId(), e);
                }
            }

        } catch (Exception e) {
            log.error("创建平台代理客户端异常: deviceClientId={}", deviceClientId, e);
        }
    }

    /**
     * 订阅平台主题
     */
    private void subscribePlatformTopics(DeviceMap mapping) {
        try {
            String productId = mapping.getPlatformProductId();
            String deviceSerialNo = mapping.getPlatformDeviceSerialNumber();

            // 订阅命令下发主题
            String commandTopic = String.format("/%s/%s/function/get", productId, deviceSerialNo);
            // 在mica-mqtt中，订阅是通过注解处理的，这里主要记录订阅关系

            // 订阅设备升级主题
            String upgradeTopic = String.format("/%s/%s/device/update", productId, deviceSerialNo);

            log.debug("为平台客户端 {} 订阅主题: {}, {}",
                    mapping.getPlatformClientId(), commandTopic, upgradeTopic);

        } catch (Exception e) {
            log.error("订阅平台主题失败: {}", mapping.getPlatformClientId(), e);
            throw e;
        }
    }

    /**
     * 处理平台命令下发
     */
    @MqttClientSubscribe("/${productId}/${deviceSerialNo}/function/get")
    public void handlePlatformCommand(String topic, Map<String, String> topicVars, byte[] payload) {
        String productId = topicVars.get("productId");
        String deviceSerialNo = topicVars.get("deviceSerialNo");

        log.info("收到平台命令下发: productId={}, deviceSerialNo={}", productId, deviceSerialNo);

        try {
            // 1. 根据平台信息找到对应的设备映射
            DeviceMap mapping = findDeviceMappingByPlatformInfo(productId, deviceSerialNo);
            if (mapping == null) {
                log.error("未找到平台设备映射: productId={}, deviceSerialNo={}", productId, deviceSerialNo);
                return;
            }

            // 2. 转换消息格式 (平台 -> 设备)
            MessageTransformResult messageResult = messageTransformService.platformToDevice(payload, mapping);

            // 3. 转换主题格式 (平台 -> 设备)
            TopicTransformResult topicResult = topicMappingService.transformPlatformToDevice(topic, mapping);

            // 4. 通过MQTT Server转发给设备
            mqttServerService.sendToDevice(mapping.getClientClientId(),
                    topicResult.getDeviceTopic(),
                    messageResult.getDeviceMessage());

            // 5. 记录监控流水
            recordMessageFlow(mapping, topic, topicResult.getDeviceTopic(), payload, "down", "success");

            log.debug("平台命令转发成功: {} -> {}", mapping.getPlatformClientId(), mapping.getClientClientId());

        } catch (Exception e) {
            log.error("处理平台命令下发失败: productId={}, deviceSerialNo={}", productId, deviceSerialNo, e);
            // 记录失败流水
            recordFailedMessageFlow(productId, deviceSerialNo, topic, payload, "down", e.getMessage());
        }
    }

    /**
     * 处理平台设备升级
     */
    @MqttClientSubscribe("/${productId}/${deviceSerialNo}/device/update")
    public void handlePlatformUpgrade(String topic, Map<String, String> topicVars, byte[] payload) {
        String productId = topicVars.get("productId");
        String deviceSerialNo = topicVars.get("deviceSerialNo");

        log.info("收到平台设备升级: productId={}, deviceSerialNo={}", productId, deviceSerialNo);

        try {
            DeviceMap mapping = findDeviceMappingByPlatformInfo(productId, deviceSerialNo);
            if (mapping == null) {
                log.error("未找到平台设备映射: productId={}, deviceSerialNo={}", productId, deviceSerialNo);
                return;
            }

            MessageTransformResult messageResult = messageTransformService.platformToDevice(payload, mapping);
            TopicTransformResult topicResult = topicMappingService.transformPlatformToDevice(topic, mapping);

            mqttServerService.sendToDevice(mapping.getClientClientId(),
                    topicResult.getDeviceTopic(),
                    messageResult.getDeviceMessage());

            recordMessageFlow(mapping, topic, topicResult.getDeviceTopic(), payload, "down", "success");

            log.debug("平台升级通知转发成功: {} -> {}", mapping.getPlatformClientId(), mapping.getClientClientId());

        } catch (Exception e) {
            log.error("处理平台设备升级失败: productId={}, deviceSerialNo={}", productId, deviceSerialNo, e);
            recordFailedMessageFlow(productId, deviceSerialNo, topic, payload, "down", e.getMessage());
        }
    }

    /**
     * 转发设备消息到平台
     */
    @Override
    public boolean forwardToPlatform(String deviceClientId, String deviceTopic, byte[] payload) {
        try {
            log.debug("转发设备消息到平台: deviceClientId={}, topic={}", deviceClientId, deviceTopic);

            // 1. 根据消息内容确定目标平台客户端
            DeviceMap targetMapping = determineTargetMapping(deviceClientId, payload);
            if (targetMapping == null) {
                log.error("无法确定目标平台映射: deviceClientId={}", deviceClientId);
                return false;
            }

            // 2. 转换主题格式 (设备 -> 平台)
            TopicTransformResult topicResult = topicMappingService.transformDeviceToPlatform(deviceTopic, targetMapping);

            // 3. 转换消息格式 (设备 -> 平台)
            MessageTransformResult messageResult = messageTransformService.deviceToPlatform(payload, targetMapping);

            // 4. 发布到平台
            mqttClientTemplate.publish(topicResult.getPlatformTopic(),
                    messageResult.getPlatformMessage(),
                    MqttQoS.valueOf(1),  // QoS 1
                    false); // retain

            // 5. 记录监控流水
            recordMessageFlow(targetMapping, topicResult.getPlatformTopic(), deviceTopic,
                    messageResult.getPlatformMessage(), "up", "success");

            log.debug("设备消息转发平台成功: {} -> {}", deviceClientId, targetMapping.getPlatformClientId());
            return true;

        } catch (Exception e) {
            log.error("转发设备消息到平台失败: deviceClientId={}", deviceClientId, e);
            recordFailedMessageFlow(deviceClientId, "平台clientId",  deviceTopic, payload, "up", e.getMessage());
            return false;
        }
    }

    /**
     * 根据平台信息查找设备映射
     */
    private DeviceMap findDeviceMappingByPlatformInfo(String productId, String deviceSerialNo) {
        // 先尝试从缓存中查找
        List<DeviceMap> allMappings = deviceMappingService.getAllDeviceMappings();
        for (DeviceMap mapping : allMappings) {
            if (productId.equals(mapping.getPlatformProductId()) &&
                    deviceSerialNo.equals(mapping.getPlatformDeviceSerialNumber())) {
                return mapping;
            }
        }
        return null;
    }

    /**
     * 根据消息内容确定目标平台映射
     */
    private DeviceMap determineTargetMapping(String clientId, byte[] payload) {
        try {
            String payloadStr = new String(payload, StandardCharsets.UTF_8);

            // 从消息中解析子设备索引
            String deviceIndex = extractDeviceIndexFromPayload(payloadStr);

            // 根据clientId和deviceIndex获取对应的映射
            DeviceMap mapping = deviceMappingService.getDeviceMapping(clientId, deviceIndex);
            if (mapping != null) {
                return mapping;
            }

            // 如果找不到特定索引的映射，返回网关映射
            return deviceMappingService.getGatewayMapping(clientId);

        } catch (Exception e) {
            log.error("确定目标平台映射失败: clientId={}", clientId, e);
            return deviceMappingService.getGatewayMapping(clientId);
        }
    }

    /**
     * 从消息中提取设备索引
     */
    private String extractDeviceIndexFromPayload(String payload) {
        try {
            // 根据实际消息格式解析index字段
            // 这里假设消息格式为JSON，包含index字段
            if (payload.contains("\"index\"")) {
                return extractJsonField(payload, "index");
            }
        } catch (Exception e) {
            log.error("提取设备索引失败", e);
        }
        return null;
    }

    /**
     * 简单的JSON字段提取
     */
    private String extractJsonField(String json, String field) {
        try {
            String fieldPattern = "\"" + field + "\":\"(.*?)\"";
            java.util.regex.Pattern pattern = java.util.regex.Pattern.compile(fieldPattern);
            java.util.regex.Matcher matcher = pattern.matcher(json);
            if (matcher.find()) {
                return matcher.group(1);
            }

            // 尝试数字类型的字段
            fieldPattern = "\"" + field + "\":(\\d+)";
            pattern = java.util.regex.Pattern.compile(fieldPattern);
            matcher = pattern.matcher(json);
            if (matcher.find()) {
                return matcher.group(1);
            }
        } catch (Exception e) {
            log.error("提取JSON字段失败: field={}", field, e);
        }
        return null;
    }

    /**
     * 记录消息流水
     */
    private void recordMessageFlow(DeviceMap mapping, String platformTopic, String deviceTopic,
                                   byte[] payload, String direction, String status) {
        try {
            MessageFlowRecord record = new MessageFlowRecord();
            record.setClientClientId(mapping.getClientClientId());
            record.setPlatformClientId(mapping.getPlatformClientId());
            record.setDirection(direction);
            record.setDeviceTopic(deviceTopic);
            record.setPlatformTopic(platformTopic);
            record.setMessageContent(new String(payload, StandardCharsets.UTF_8));
            record.setMessageSize(payload.length);
            record.setStatus(status);
            monitorService.recordMessageFlow(record);
        } catch (Exception e) {
            log.error("记录消息流水失败", e);
        }
    }

    /**
     * 记录失败消息流水
     */
    private void recordFailedMessageFlow(String identifier1, String identifier2, String topic,
                                         byte[] payload, String direction, String error) {
        try {
            MessageFlowRecord record = new MessageFlowRecord();
            record.setClientClientId(identifier1);
            record.setPlatformClientId(identifier2);
            record.setDirection(direction);
            record.setDeviceTopic(topic);
            record.setPlatformTopic(topic);
            record.setMessageContent(new String(payload, StandardCharsets.UTF_8));
            record.setMessageSize(payload.length);
            record.setStatus("failed");
            record.setErrorMessage(error);
            monitorService.recordMessageFlow(record);
        } catch (Exception e) {
            log.error("记录失败消息流水失败", e);
        }
    }

    /**
     * 销毁平台代理客户端
     */
    @Override
    public void destroyPlatformClients(String deviceClientId) {
        try {
            log.info("销毁设备 {} 的平台代理客户端", deviceClientId);

            // 获取设备的所有映射关系
            List<DeviceMap> deviceMappings = deviceMappingService.getDeviceMappingsByClientId(deviceClientId);
            if (deviceMappings != null) {
                for (DeviceMap mapping : deviceMappings) {
                    monitorService.recordPlatformClientDisconnect(mapping.getPlatformClientId());
                }
            }

            // 从映射表中移除
            devicePlatformClientsMap.remove(deviceClientId);

            log.info("设备 {} 的平台代理客户端销毁完成", deviceClientId);

        } catch (Exception e) {
            log.error("销毁平台代理客户端异常: deviceClientId={}", deviceClientId, e);
        }
    }

    /**
     * 获取平台客户端状态
     */
    @Override
    public Map<String, List<PlatformClientInfo>> getPlatformClientsStatus() {
        return new ConcurrentHashMap<>(devicePlatformClientsMap);
    }

    /**
     * 平台客户端信息
     */
    public static class PlatformClientInfo {
        private String platformClientId;
        private String deviceClientId;
        private long connectTime;
        private String status;

        // getter and setter methods
        public String getPlatformClientId() { return platformClientId; }
        public void setPlatformClientId(String platformClientId) { this.platformClientId = platformClientId; }
        public String getDeviceClientId() { return deviceClientId; }
        public void setDeviceClientId(String deviceClientId) { this.deviceClientId = deviceClientId; }
        public long getConnectTime() { return connectTime; }
        public void setConnectTime(long connectTime) { this.connectTime = connectTime; }
        public String getStatus() { return status; }
        public void setStatus(String status) { this.status = status; }
    }
}
